package cc.autoapi.pucong.auto.flow.core.flow.execNode.exec.handler;

import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.api.ApiHandlerMsgDto;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.log.LogNodeHandlerMsgDto;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.node.*;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.task.ApiExecResult;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.eventBus.ISubscriberAbstract;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.exec.DataBus;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.exec.publish.ProcessExecApiPublish;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.exec.publish.ProcessExecLogPublish;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.LogLevelTypeEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.MsgTypeEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.NodeTypeEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.RunStatusEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.task.AbstractTask;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.task.TaskFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 节点执行处理器
 */
@Slf4j
public class ProcessExecNodeHandler extends ISubscriberAbstract<NodeHandlerMsgDto> {

    public ProcessExecNodeHandler() {
        super();
    }

    @Override
    public Integer getIndexSort() {
        return 0;
    }

    @Override
    public String getMessageType() {
        return MsgTypeEnum.NODE_MSG.getType();
    }

    @Override
    public void receiveMessage(NodeHandlerMsgDto dto) {
        // 判断api状态
        ApiExecResult apiStatus = DataBus.getApiStatus(dto.getInstanceId());
        if (apiStatus.getRunStatus() == RunStatusEnum.执行失败 || apiStatus.getRunStatus() == RunStatusEnum.执行成功) {
            return;
        }

        String apiId = dto.getApiId();
        String instanceId = dto.getInstanceId();
        String nodeId = dto.getNodeId();
        Boolean isIgnore = dto.getIsIgnore();
        // 如果是忽略执行，则需要判断父级节点，有一个不是忽略执行，则执行
        if (isIgnore) {
            List<ExecNode> parentNodeList = getParentNodeList(apiId, instanceId, nodeId);
            for (ExecNode parentNode : parentNodeList) {
                if (!parentNode.getIsIgnore()
                        && !parentNode.getNodeType().equals(NodeTypeEnum.SWITCH.getKey())
                        && !parentNode.getNodeType().equals("aiToolsTask")) {
                    isIgnore = false;
                    break;
                }
            }
        }
        ProcessExecApiPublish apiPublish = DataBus.getApiPublish();
        ProcessExecLogPublish nodeLogPublish = DataBus.getNodeLogPublish();

        List<TaskNode> taskNodeList = DataBus.getTaskNodeList(apiId);
        TaskNode taskNode = taskNodeList.stream().filter(node -> node.getUuid().equals(nodeId)).findFirst().get();
        if (taskNode == null) {
            String logContent = String.format("未找到执行节点,instanceId:{},nodeId:{}", instanceId, nodeId);
            nodeLogPublish.publicMessageByAsync(LogNodeHandlerMsgDto.error(apiId, instanceId, nodeId, null));
            nodeLogPublish.publicMessageByAsync(LogNodeHandlerMsgDto.log(apiId, instanceId, nodeId, null, LogLevelTypeEnum.ERROR, logContent));
            apiPublish.publicMessageByAsync(ApiHandlerMsgDto.error(apiId, instanceId, logContent));
            apiPublish.publicMessageByAsync(ApiHandlerMsgDto.log(apiId, instanceId, LogLevelTypeEnum.ERROR, logContent));
            return;
        }

        ExecNode execNode = new ExecNode(taskNode);
        if (NodeTypeEnum.FOR.getKey().equals(taskNode.getNodeType())) {
            execNode = new ExecNodeFor(taskNode);
        } else if (NodeTypeEnum.PROCESS.getKey().equals(taskNode.getNodeType())) {
            execNode = new ExecNodeProcess(taskNode);
        }
        execNode.setIsIgnore(isIgnore);
        DataBus.addExecNodeList(instanceId, execNode);
        AbstractTask task = TaskFactory.getTask(execNode.getNodeType());
        task.setApiId(apiId);
        task.setInstanceId(instanceId);
        task.setExecNode(execNode);

        try {
            // 设置并校验参数值
            if (!isIgnore) {
                task.checkAndSetParamsValue();
            }
            // 设置为开始执行状态
            nodeLogPublish.publicMessageByAsync(LogNodeHandlerMsgDto.start(apiId, instanceId, nodeId, execNode.getInstanceNodeId()));
            task.handle();
            // 成功通知
            nodeLogPublish.publicMessageByAsync(LogNodeHandlerMsgDto.ok(apiId, instanceId, nodeId, execNode.getInstanceNodeId(), isIgnore));
        } catch (Exception e) {
            String stackTrace = ExceptionUtils.getStackTrace(e);
            String logContent = String.format("节点执行失败,api实例id：%s，节点名称:%s,\r error: %s", instanceId, execNode.getNodeName(), stackTrace);
            log.error(logContent, e);
            apiPublish.publicMessageByAsync(ApiHandlerMsgDto.error(apiId, instanceId, logContent));
            apiPublish.publicMessageByAsync(ApiHandlerMsgDto.log(apiId, instanceId, LogLevelTypeEnum.ERROR, logContent));
            nodeLogPublish.publicMessageByAsync(LogNodeHandlerMsgDto.error(apiId, instanceId, nodeId, execNode.getInstanceNodeId()));
            nodeLogPublish.publicMessageByAsync(LogNodeHandlerMsgDto.log(apiId, instanceId, nodeId, execNode.getInstanceNodeId(), LogLevelTypeEnum.ERROR, logContent));
            execNode.setNodeExecResultError(logContent);
        }
    }

    /**
     * 获取父级节点
     *
     * @param apiId
     * @param instanceId
     * @param nodeId
     * @return
     */
    private List<ExecNode> getParentNodeList(String apiId, String instanceId, String nodeId) {
        List<ExecNode> brotherNodeList = new ArrayList<>();
        List<ExecNode> execNodeList = DataBus.getExecNodeList(instanceId);
        List<TaskNodeRelation> taskNodeRelationList = DataBus.getTaskNodeRelationList(apiId);
        List<String> execNodeIds = taskNodeRelationList.stream().filter(relation -> relation.getNodeToId().equals(nodeId)).map(relation -> relation.getNodeFromId()).collect(Collectors.toList());
        for (String id : execNodeIds) {
            ExecNode execNode = execNodeList.stream().filter(node -> node.getUuid().equals(id)).findFirst().get();
            brotherNodeList.add(execNode);
        }
        return brotherNodeList;
    }
}
